#include "FAudio.h"

FAudio::FAudio() {
}

FAudio::~FAudio() {
}

int FAudio::play() {
    isPlaying = 1;
    pthread_create(&p_tid, NULL, playAudio, this);
    return 0;
}

void FAudio::setCodecContext(AVCodecContext *codec_ctx, int stream_idx) {
    this->codec_ctx = codec_ctx;
    this->stream_idx = stream_idx;
    initSwrContext();
}

int FAudio::initSwrContext() {
    swrContext = swr_alloc();
    swr_alloc_set_opts(swrContext,
                       AV_CH_LAYOUT_STEREO, // 立体声
                       AV_SAMPLE_FMT_S16,   // 16 位
                       codec_ctx->sample_rate,
                       codec_ctx->channel_layout,
                       codec_ctx->sample_fmt,
                       codec_ctx->sample_rate, 0, NULL);
    swr_init(swrContext);
    buf_size = 44100 * 2;
    out_buf = (uint8_t *) av_malloc(buf_size);
    return 0;
}

// 创建播放器
int FAudio::startPlayer() {
    SLresult rs;
    rs = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("slCreateEngine occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("engineObject->Realize occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineItf);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("engineObject->GetInterface(engine) occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*engineItf)->CreateOutputMix(engineItf, &mixObject, 0, 0, 0);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("engineItf->CreateOutputMix occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*mixObject)->Realize(mixObject, SL_BOOLEAN_FALSE);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("mixObject->Realize occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*mixObject)->GetInterface(mixObject, SL_IID_ENVIRONMENTALREVERB, &mixItf);
    if (rs == SL_RESULT_SUCCESS) {
        SLEnvironmentalReverbSettings settings = SL_I3DL2_ENVIRONMENT_PRESET_DEFAULT;
        (*mixItf)->SetEnvironmentalReverbProperties(mixItf, &settings);
    }
    SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                           2};
    SLDataFormat_PCM pcmFmt = {SL_DATAFORMAT_PCM,
                               2,
                               SL_SAMPLINGRATE_44_1,
                               SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
                               SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, // 立体声
                               SL_BYTEORDER_LITTLEENDIAN
    };
    SLDataSource dataSource = {&buffer_queue, &pcmFmt};
    SLDataLocator_OutputMix locatorOutputMix = {SL_DATALOCATOR_OUTPUTMIX, mixObject};
    SLDataSink dataSink = {&locatorOutputMix, NULL};
    SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME};
    SLboolean reqs[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
    rs = (*engineItf)->CreateAudioPlayer(engineItf, &playerObject, &dataSource,
                                         &dataSink, 3, ids,
                                         reqs);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("engineItf->CreateAudioPlayer occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("playerObject->Realize occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playItf);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("playerObject->GetInterface(play) occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &bufferQueue);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("playerObject->GetInterface(buffer_queue) occurs error, errorCode: %d", rs);
        return 1;
    }
    rs = (*bufferQueue)->RegisterCallback(bufferQueue, bufferQueueCallback, this);
    if (rs != SL_RESULT_SUCCESS) {
        LOG_E("playItf->RegisterCallback occurs error, errorCode: %d", rs);
        return 1;
    }
    (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
    bufferQueueCallback(bufferQueue, this);
    return 0;
}

void bufferQueueCallback(SLAndroidSimpleBufferQueueItf buf_queue, void *arg) {
    FAudio *fAudio = (FAudio *) arg;
    AVPacket *packet = (AVPacket *) av_mallocz(sizeof(AVPacket));
    AVFrame *frame = av_frame_alloc();
    while (fAudio->isPlaying) {
        fAudio->pop(packet);
        if (packet->pts != AV_NOPTS_VALUE) {
            // 修正播放时间
            fAudio->clock = packet->pts * av_q2d(fAudio->time_base);
        }
        double sample_rate = fAudio->codec_ctx->sample_rate;
        int got_frame;
        avcodec_decode_audio4(fAudio->codec_ctx, frame, &got_frame, packet);
        if (got_frame) {
            swr_convert(fAudio->swrContext, &(fAudio->out_buf), fAudio->buf_size,
                        (const uint8_t **) frame->data,
                        frame->nb_samples);
            int buf_size = av_samples_get_buffer_size(NULL,
                                                      frame->channels,
                                                      frame->nb_samples,
                                                      AV_SAMPLE_FMT_S16,
                                                      0);
            // 一帧的播放时间 = 每一帧单通道的采样个数 / 采样率
            double clock = frame->nb_samples / sample_rate;
            // 计算当前的播放时间
            fAudio->clock += clock;
            LOG_I("帧 %f，%f", clock, fAudio->clock);
            if (fAudio->out_buf != NULL && buf_size > 0) {
                SLresult rs = (*buf_queue)->Enqueue(buf_queue, fAudio->out_buf, buf_size);
                LOG_I("buffer_queue->Enqueue %d", rs);
            }
            break;
        }
    }
    av_frame_free(&frame);
    av_free(packet);
}

void *playAudio(void *arg) {
    FAudio *fAudio = (FAudio *) arg;
    fAudio->startPlayer();
    pthread_exit(0);
}

void FAudio::stop() {
    FMedia::stop();
    if (playItf) {
        (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
    }
}

void FAudio::release() {
    FMedia::release();
    swr_close(swrContext);
    swr_free(&swrContext);
    if (bufferQueue) {
        (*bufferQueue)->Clear(bufferQueue);
    }
    if (mixObject) {
        (*mixObject)->Destroy(mixObject);
        mixItf = NULL;
        mixObject = NULL;
    }
    if (playerObject) {
        (*playerObject)->Destroy(playerObject);
        playItf = NULL;
        playerObject = NULL;
    }
    if (engineObject) {
        (*engineObject)->Destroy(engineObject);
        engineItf = NULL;
        engineObject = NULL;
    }
}

